﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;
using Red.Core;
using Utils;

namespace Red.Controls.Diagrams.ViewModels
{
    /// <summary>
    /// Defines a connector (aka connection point) that can be attached to a node and is used to connect the node to another node.
    /// </summary>
    public class ConnectorViewModel : ViewModel
    {
        #region Data

        /// <summary>
        /// The connections that are attached to this connector, or null if no connections are attached.
        /// </summary>
        private ImpObservableCollection<ConnectionViewModel> _attachedConnections;

        /// <summary>
        /// The hotspot (or center) of the connector.
        /// This is pushed through from ConnectorItem in the UI.
        /// </summary>
        private Point _hotspot;

        #endregion Data

        public ConnectorViewModel( string name, string id, IProperty property, bool isInherited, Color background, bool isValid = true )
        {
            Name = name;
            Type = ConnectorType.Undefined;
            ID = id;
            Property = property;
            IsInherited = isInherited;
            Background = background;
            IsValid = isValid;
        }

        /// <summary>
        /// Specifies if this connector is valid (for example when property binding is wrong).
		/// It can't change its value in runtime.
        /// This is pushed through from ConnectorItem in the UI.
        /// </summary>
        public bool IsValid { get; set; }

        public bool IsInherited { get; set; }

        /// <summary>
        /// The name of the connector.
        /// </summary>
        public string Name
        {
            get;
            private set;
        }

        /// <summary>
        /// Defines the type of the connector.
        /// </summary>
        public ConnectorType Type
        {
            get;
            internal set;
        }

        /// <summary>
        /// Returns 'true' if the connector connected to another node.
        /// </summary>
        public bool IsConnected
        {
            get
            {
                foreach ( var connection in AttachedConnections )
                {
                    if ( connection.SourceConnector != null &&
                        connection.DestConnector != null )
                    {
                        return true;
                    }
                }

                return false;
            }
        }

        /// <summary>
        /// The connections that are attached to this connector, or null if no connections are attached.
        /// </summary>
        public ImpObservableCollection<ConnectionViewModel> AttachedConnections
        {
            get
            {
                if ( _attachedConnections == null )
                {
                    _attachedConnections = new ImpObservableCollection<ConnectionViewModel>();
                    _attachedConnections.ItemsAdded += new EventHandler<CollectionItemsChangedEventArgs>( attachedConnections_ItemsAdded );
                    _attachedConnections.ItemsRemoved += new EventHandler<CollectionItemsChangedEventArgs>( attachedConnections_ItemsRemoved );
                }

                return _attachedConnections;
            }
        }

        /// <summary>
        /// The parent node that the connector is attached to, or null if the connector is not attached to any node.
        /// </summary>
        public NodeViewModel ParentNode
        {
            get;
            internal set;
        }

        /// <summary>
        /// Unique ID of this connector.
        /// </summary>
        public string ID { get; private set; }

        /// <summary>
        /// Property bound to this connector.
        /// </summary>
        public IProperty Property { get; set; }

        /// <summary>
        /// The hotspot (or center) of the connector.
        /// This is pushed through from ConnectorItem in the UI.
        /// </summary>
        public Point Hotspot
        {
            get
            {
                return _hotspot;
            }
            set
            {
                if ( _hotspot == value )
                {
                    return;
                }

                _hotspot = value;

                OnHotspotUpdated();
                OnPropertyChanged();
            }
        }

        public List<ConnectorMenuItem> ContextMenuItems
        {
            get
            {
                return GetContextMenuItems();
            }
        }

        /// <summary>
        /// Background color of connector.
        /// </summary>
        public Color Background { get; private set; }

        /// <summary>
        /// Event raised when the connector hotspot has been updated.
        /// </summary>
        public event EventHandler<EventArgs> HotspotUpdated;

        #region Private Methods

        /// <summary>
        /// Debug checking to ensure that no connection is added to the list twice.
        /// </summary>
        private void attachedConnections_ItemsAdded( object sender, CollectionItemsChangedEventArgs e )
        {
            foreach ( ConnectionViewModel connection in e.Items )
            {
                connection.ConnectionChanged += new EventHandler<EventArgs>( connection_ConnectionChanged );
            }

            OnPropertyChanged( "ContextMenuItems" );
            if ( ( AttachedConnections.Count - e.Items.Count ) == 0 )
            {
                // 
                // The first connection has been added, notify the data-binding system that
                // 'IsConnected' should be re-evaluated.
                //                
                OnPropertyChanged( "IsConnected" );
            }
        }

        /// <summary>
        /// Event raised when connections have been removed from the connector.
        /// </summary>
        private void attachedConnections_ItemsRemoved( object sender, CollectionItemsChangedEventArgs e )
        {
            foreach ( ConnectionViewModel connection in e.Items )
            {
                connection.ConnectionChanged -= new EventHandler<EventArgs>( connection_ConnectionChanged );
            }

            OnPropertyChanged( "ContextMenuItems" );
            if ( AttachedConnections.Count == 0 )
            {
                // 
                // No longer connected to anything, notify the data-binding system that
                // 'IsConnected' should be re-evaluated.
                //
                OnPropertyChanged( "IsConnected" );
            }
        }

        /// <summary>
        /// Event raised when a connection attached to the connector has changed.
        /// </summary>
        private void connection_ConnectionChanged( object sender, EventArgs e )
        {
            OnPropertyChanged( "IsConnected" );
            OnPropertyChanged( "ContextMenuItems" );
        }

        /// <summary>
        /// Called when the connector hotspot has been updated.
        /// </summary>
        private void OnHotspotUpdated()
        {
            if ( HotspotUpdated != null )
                HotspotUpdated( this, EventArgs.Empty );
        }

        /// <summary>
        /// Returns list of menu items with connections that can be removed via context menu for this connector.
        /// </summary>
        private List<ConnectorMenuItem> GetContextMenuItems()
        {
            var menuItems = new List<ConnectorMenuItem>();

            if ( AttachedConnections.Count > 1 )
            {
                menuItems.Add( new RemoveAttachedConnectionsMenuItem { Connector = this } );
                menuItems.Add( new SeparatorMenuItem() );
            }

            foreach ( var attachedConnection in AttachedConnections )
            {
                var connectorMenuItem = new RemoveConnectionMenuItem { Connection = attachedConnection };
                var otherConnector = attachedConnection.DestConnector == this
                    ? attachedConnection.SourceConnector
                    : attachedConnection.DestConnector;                
                // other connector can be null after removal of connection, that's why we are skipping this item.
                if ( otherConnector == null )
                    continue;
                connectorMenuItem.Header = string.Format( "Remove connection with {0}.{1}", otherConnector.ParentNode.Name, otherConnector.Name );
                menuItems.Add( connectorMenuItem );
            }

            return menuItems;
        }

        #endregion Private Methods
    }

    /// <summary>
    /// Base class for connector menu items that are displayed in ContextMenu.
    /// </summary>
    public class ConnectorMenuItem
    {
        #region Properties

        public string Header { get; set; }

        #endregion Properties
    }

    /// <summary>
    /// Class that represents each connection MenuItem that can be removed via context menu on each connector that has any attached connections.
    /// </summary>
    public class RemoveConnectionMenuItem : ConnectorMenuItem
    {
        #region Properties

        public ConnectionViewModel Connection { get; set; }        

        #endregion Properties
    }

    /// <summary>
    /// If there is more than one connection to disconnect we are adding "remove all connections" MenuItem.
    /// </summary>
    public class RemoveAttachedConnectionsMenuItem : ConnectorMenuItem
    {
        #region Properties

        public ConnectorViewModel Connector { get; set; }

        #endregion Properties

        #region C-tor

        public RemoveAttachedConnectionsMenuItem()
        {
            Header = "Remove all connections";
        }

        #endregion C-tor
    }

    /// <summary>
    /// Class that represents <Separator/> menu item.
    /// </summary>
    public class SeparatorMenuItem : ConnectorMenuItem
    {

    }
}
